/**
 * User: alesj
 * Date: 2005.2.23
 * Time: 15:04:13
 * 
 * (C) Genera Lynx d.o.o.
 */

package com.generalynx.common.web.tags;

import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.Log;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public abstract class MaskGenerator {

    private static final Log logger = LogFactory.getLog(MaskGenerator.class);

    private static final char FIRST = 'A';
    private static final char LAST = 'z';
    public static final char RANGE = LAST - FIRST + 1;

    private static Map permutationMap = new HashMap();
    private static Map inverseMap = new HashMap();

    static {
        createPermutationMap();
    }

    public static void createPermutationMap() {
        long start = System.currentTimeMillis();
        RandomGenerator generator = new RandomGenerator(RANGE);
        for(int i = 0; i < RANGE; i++) {
            int[] permutation = generator.getNext();
            Integer key = new Integer(i);
            permutationMap.put(key, permutation);
            inverseMap.put(key, inverse(permutation));
        }
        logger.info("Creation time: " + ((System.currentTimeMillis() - start) / 1000) + "s");
    }

    private static int[] inverse(int[] permutation) {
        int n = permutation.length;
        int[] inverse = new int[n];
        for (int i = 0; i < n; i++) {
            inverse[permutation[i]] = i;
        }
        return inverse;
    }

    private static String transform(ITransformFunction function, String string) {
        char[] chars = new char[string.length()];
        for (int i = 0; i < chars.length; i++) {
            chars[i] = (char)function.transform(string.charAt(i));
        }
        return new String(chars);
    }

    private static int[] getPermutation(int seed, Map map) {
        return (int[])map.get(new Integer(seed % RANGE));
    }

    public static String mask(int seed, String string) {
        return transform(new PermutationTransformFunction(getPermutation(seed, permutationMap)), string);
    }

    public static String unmask(int seed, String string) {
        return transform(new PermutationTransformFunction(getPermutation(seed, inverseMap)), string);
    }

    private interface ITransformFunction {
        public int transform(char ch);
    }

    private static class PermutationTransformFunction implements ITransformFunction {

        int[] permutation;

        public PermutationTransformFunction(int[] permutation) {
            this.permutation = permutation;
        }

        public int transform(char ch) {
            if (ch < FIRST || ch > LAST) {
                return ch;
            } else {
                return permutation[ch - FIRST] + FIRST;
            }
        }
    }

    private static class RandomGenerator {

        private int n;
        private Random random = new Random();

        public RandomGenerator(int n) {
            this.n = n;
        }

        public int[] getNext() {
            int[] array = new int[n];
            boolean [] used = new boolean[n];
            int count = 0;
            while(count != n) {
                int x = Math.abs(random.nextInt()) % n;
                if (!used[x]) {
                    array[count++] = x;
                    used[x] = true;
                }
            }
            return array;
        }
    }
}
